package com.tax.config.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.Assert;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtTokenUtil {
    /**
     * 默认两小时的有效期
     */
    public static final long JWT_TOKEN_VALIDITY = 7200L;

    private String secret;

    private Long jwtTokenExpire;

    //秘钥加密解密都需要，所以定义成静态常量
    public static final String SIGNATURE = "tax@aiqicha.com";

    public void setJwtTokenExpire(Long jwtTokenExpire) {
        this.jwtTokenExpire = jwtTokenExpire;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public String getSecret(){
        if(StringUtils.isEmpty(this.secret)){
            log.warn("JwtTokenUtil secret is empty,set default_secret");
            return SIGNATURE;
        }
        return this.secret;
    }

    public String generateToken(JwtUserInfo userInfo) {
        Assert.notNull(userInfo, "userInfo must not be null");
        String phoneNum = userInfo.getTelephone();
        Assert.notNull(phoneNum, "phoneNum must not be null");
        return doGenerateToken(userInfo.toMap(), phoneNum);
    }

    /**
     * 1、定义token的声明信息,如过期时间Expiration、Subject、Id等
     * 2、对JWT使用的加密方式和秘钥
     *
     * @param claims
     * @param subject
     * @return
     */
    public String doGenerateToken(HashMap<String,String> claims, String subject){
        if (jwtTokenExpire == null) {
            jwtTokenExpire = JWT_TOKEN_VALIDITY;
        }
        //设置一个时间，作为令牌的过期时间，设计时间为60分钟后
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,60);
        long currentTimeMillis = System.currentTimeMillis();
        //创建jwt builder
        JWTCreator.Builder builder = JWT.create();
        //遍历map集合,将信息放到payload的Claim中
        claims.forEach((k,v)-> builder.withClaim(k,v));

        String token = builder
                .withJWTId(currentTimeMillis + "")
//               .withSubject("subject")
                .withSubject(subject)
                .withIssuedAt(new Date(System.currentTimeMillis()))
                .withExpiresAt(new Date(System.currentTimeMillis() + jwtTokenExpire * 1000))
                .sign(Algorithm.HMAC256(getSecret())); //加密

        return token;
    }

    /**
     * 验证token  DecodedJWT 为解密之后的对象 可以获取payload中添加的数据
     * @param token
     * @return
     */
    public DecodedJWT verifyToken(String token){
        //进行token的校验,注意使用同样的算法和同样的秘钥
        return JWT.require(Algorithm.HMAC256(getSecret())).build().verify(token);
    }

    /**
     * 不验证token是否过期，获取token信息
     * @param token
     * @return
     */
    public DecodedJWT decodeToken(String token){
        return JWT.decode(token);
    }


    /**
     * 验证token是否过期
     *
     * @param token
     * @return
     */
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateClaims(token);
        return expiration.before(new Date());
    }

    private Date getExpirationDateClaims(String token) {
        try{
            DecodedJWT decodedJWT = verifyToken(token);
            Map<String, Claim> claims = decodedJWT.getClaims();
            return claims.get("exp").asDate();
        }catch (Exception e){
            log.error("JwtTokenUtil.getExpirationDateClaims,{}", ExceptionUtils.getMessage(e));
        }
        return null;

    }

    /**
     * validate token
     * @param token
     * @param subject
     * @return
     */
    public Boolean validateSubFormToken(String token, String subject) {
        final String username = getSubjectFromClaims(token);
        return (username.equals(subject) && !isTokenExpired(token));
    }

    private String getSubjectFromClaims(String token) {
        try{
            DecodedJWT decodedJWT = verifyToken(token);
            Map<String, Claim> claims = decodedJWT.getClaims();
            return claims.get("sub").asString();
        }catch (Exception e){
            log.error("JwtTokenUtil.getExpirationDateClaims,{}", ExceptionUtils.getMessage(e));
        }
        return StringUtils.EMPTY;
    }

    public JwtUserInfo getJwtUserInfo(String token) {
        if(token==null){
            return null;
        }
        try{
            DecodedJWT decodedJWT = verifyToken(token);
            Map<String, Claim> claimMap = decodedJWT.getClaims();
            if (null==claimMap || claimMap.isEmpty()){
                return null;
            }
            JwtUserInfo userInfo=new JwtUserInfo();
            if (null!=claimMap.get("userId")){
                userInfo.setUserId(claimMap.get("userId").asString());
            }
            if (null!=claimMap.get("telephone")){
                userInfo.setTelephone(claimMap.get("telephone").asString());
            }
            if (null!=claimMap.get("userLoginType")){
                userInfo.setUserLoginType(claimMap.get("userLoginType").asString());
            }
            if (null!=claimMap.get("sub")){
                userInfo.setSub(claimMap.get("sub").asString());
            }
            if (null!=claimMap.get("exp")){
                userInfo.setExp(claimMap.get("exp").asInt());
            }
            if (null!=claimMap.get("jti")){
                userInfo.setJti(claimMap.get("jti").asString());
            }
            if (null!=claimMap.get("iat")){
                userInfo.setIat(claimMap.get("iat").asInt());
            }
            if (null!=claimMap.get("deviceId")){
                userInfo.setDeviceId(claimMap.get("deviceId").asString());
            }
            if (null!=claimMap.get("appSys")){
                userInfo.setAppSys(claimMap.get("appSys").asString());
            }
            if (null!=claimMap.get("appVersion")){
                userInfo.setAppVersion(claimMap.get("appVersion").asString());
            }
            return userInfo;
        }catch (Exception e){
            log.error("JwtTokenUtil.getJwtUserInfo,{}",ExceptionUtils.getMessage(e));
        }
        return null;
    }
}
